﻿/*========================== 
 * @author 郑金泉
 * @desc 方法处理类
 * ========================= */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Xml;
using System.IO;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Obsidian.Config;
using Obsidian.Utils;
using Obsidian.Web;
using Obsidian.Runtime;
using System.Reflection;

namespace Obsidian.Action
{
    public class ActReqHandler
    {

        public const string STATUS_OK = "OK";
        public const string STATUS_ERROR = "ERROR";
        public const string STATUS_SESSION_KEY_INVALID = "SESSION_KEY_INVALID";

        private const string PN_APPKEY = "appKey";
        private const string PN_APPSECRET = "appSecret";
        private const string PN_SIGN = "sign";
        private const string PN_TIMESTAMP = "timeStamp";
        private const string PN_SESSIONKEY = "sessionKey";
        private const string PN_ACTION = "action";
        private const string PN_FORMAT = "format";

        private const String MSG_DATA_FORMAT_ERROR = "数据格式不正确";
        private const String MSG_PARAM_ERROR = "参数 {0} 错误";
        private const String MSG_SING_VERFIY_FAIL = "签名验证失败";
        private const String MSG_APPKEY_NO_EXISTS = "appKey:{0} 不存在";
        private const String MSG_ACTION_NO_EXISTS = "action:{0} 不存在";
        private const String MSG_TIMESTAMP_TIMEOUT = "timestamp过期";

        private ActRequestType _requestType = ActRequestType.Normal;
        private ActResponseType _responseType = ActResponseType.Normal;
        private ActFormatType _formatType = ActFormatType.Json;

        private ApiConfig.ApiInfo _apiInfo;
        private string _status = STATUS_OK;
        private string _sessionKey = "";
        private string _message = "";
        private DateTime _remoteDateTime;
        private List<ActionRequest> _requests = new List<ActionRequest>();
        /** 是否是错误的请求 */
        private bool _isBadRequest = false;
        private string _responseText = null;


        public ApiConfig.ApiInfo ApiInfo
        {
            get { return this._apiInfo; }
        }

        public bool IsBadRequest
        {
            get { return this._isBadRequest; }
        }

        public List<ActionRequest> Requests
        {
            get { return this._requests; }
        }

        public string Status
        {
            get { return this._status; }
            set { this._status = value; }
        }

        public string SessionKey
        {
            get { return this._sessionKey; }
        }

        public string Message
        {
            get { return this._message; }
        }

        public DateTime RemoteDateTime
        {
            get { return this._remoteDateTime; }
        }

        public string ResponseText
        {
            get { return this._responseText; }
        }

        public ActRequestType RequestType
        {
            get { return this._requestType; }
        }

        public ActResponseType ResponseType
        {
            get { return this._responseType; }
        }

        public ActReqHandler()
        {
            this.PrepareApiInfo(null);
        }

        public ActReqHandler(string appKey)
        {
            this.PrepareApiInfo(appKey);
        }

        public ActReqHandler(Stream stream)
        {
            this.ReadRequests(stream, null);
        }

        public ActReqHandler(Stream stream, string appKey)
        {
            this.ReadRequests(stream, appKey);
        }


        public ActionRequest AddRequest(string action)
        {
            ActionRequest req = new ActionRequest(action);
            this.AddRequest(req);
            return req;
        }

        public void AddRequest(ActionRequest req)
        {
            this._requests.Add(req);
        }

        private void PrepareApiInfo(string appKey)
        {
            ApiConfig.ApiInfo apiInfo;
            if (String.IsNullOrEmpty(appKey))
            {
                apiInfo = ApiConfig.DefaultApiInfo;
                if (apiInfo == null)
                    throw new Exception("不存在apiInfo默认配置信息");
                this._apiInfo = apiInfo;
            }
            else
            {
                apiInfo = ApiConfig.GetApiInfo(appKey);
                if (apiInfo == null)
                    throw new Exception("不存在" + appKey + "配置信息");
                this._apiInfo = apiInfo;
            }
        }

        #region ReadRequests

        private void ReadRequests(Stream stream, string appKey)
        {
            string strReqCont = IOUtil.StreamToString(stream);
            this.ReadRequests(strReqCont, appKey);
        }
        private void ReadRequests(string strReqCont, string appKey)
        {
            if (strReqCont.StartsWith("#"))
            {
                this._requestType = ActRequestType.Compress;
                this._responseType = ActResponseType.Compress;
                this._isBadRequest = ReadActionRequestsForCompress(strReqCont, appKey);
            }
            else
            {
                this._requestType = ActRequestType.Normal;
                this._responseType = ActResponseType.Normal;
                this._isBadRequest = ReadActionRequestForNormal(strReqCont, appKey);
            }
        }


        private bool ReadActionRequestForNormal(string strReqCont, string appKey)
        {
            List<string> listParamPairs = new List<string>();
            string[] arrStrs = strReqCont.Split('&');
            Dictionary<string, object> dict = new Dictionary<string, object>(
                arrStrs.Length, StringComparer.OrdinalIgnoreCase);
            string signStart = PN_SIGN + "=";
            foreach (string str in arrStrs)
            {
                if (!str.StartsWith(signStart, StringComparison.OrdinalIgnoreCase))
                    listParamPairs.Add(str);

                string[] kvPair = str.Split('=');
                if (kvPair.Length != 2)
                    continue;
                string key = kvPair[0];
                string val = kvPair[1];
                dict[key] = HttpUtility.UrlDecode(val);
            }

            object dictVal;
            string timestamp;
            if (!dict.TryGetValue(PN_TIMESTAMP, out dictVal) || String.IsNullOrEmpty(timestamp = Convert.ToString(dictVal)))
            {
                this.Error(String.Format(MSG_PARAM_ERROR, PN_TIMESTAMP));
                return false;
            }

            string sign;
            if (!dict.TryGetValue(PN_SIGN, out dictVal) || String.IsNullOrEmpty(sign = Convert.ToString(dictVal)))
            {
                this.Error(String.Format(MSG_PARAM_ERROR, PN_SIGN));
                return false;
            }

            string action;
            if (!dict.TryGetValue(PN_ACTION, out dictVal) || String.IsNullOrEmpty(action = Convert.ToString(dictVal)))
            {
                this.Error(String.Format(MSG_PARAM_ERROR, PN_ACTION));
                return false;
            }

            string format;
            if (dict.TryGetValue(PN_FORMAT, out dictVal) && !String.IsNullOrEmpty(format = Convert.ToString(dictVal)))
            {
                if (format.Equals("json", StringComparison.OrdinalIgnoreCase))
                    _formatType = ActFormatType.Json;
                else if (format.Equals("xml", StringComparison.OrdinalIgnoreCase))
                    _formatType = ActFormatType.Xml;
                else
                {
                    this.Error(String.Format(MSG_PARAM_ERROR, PN_FORMAT));
                    return false;
                }
            }

            if (dict.TryGetValue(PN_SESSIONKEY, out dictVal))
                this._sessionKey = Convert.ToString(dictVal);


            string reqAppKey = null;
            if (dict.TryGetValue(PN_APPKEY, out dictVal))
                reqAppKey = Convert.ToString(dictVal);
            if (String.IsNullOrEmpty(appKey))
                appKey = reqAppKey;
            if (String.IsNullOrEmpty(appKey))
            {
                this.Error(String.Format(MSG_PARAM_ERROR, PN_APPKEY));
                return false;
            }
            ApiConfig.ApiInfo apiInfo = ApiConfig.GetApiInfo(appKey);
            if (apiInfo == null)
            {
                this.Error(String.Format(MSG_APPKEY_NO_EXISTS, appKey));
                return false;
            }
            this._apiInfo = apiInfo;

            if (apiInfo.NeedVerify)
            {
                //timestamp
                string tsErrorMsg = this.VerifyTimestamp(timestamp);
                if (!String.IsNullOrEmpty(tsErrorMsg))
                {
                    this.Error(tsErrorMsg);
                    return false;
                }

                //sign
                string gSgin = this.GenerateSignForNormal(listParamPairs);
                if (!sign.Equals(gSgin))
                {
                    this.Error(MSG_SING_VERFIY_FAIL);
                    return false;
                }

            }

            if (dict.ContainsKey(PN_APPKEY))
                dict.Remove(PN_APPKEY);
            if (dict.ContainsKey(PN_TIMESTAMP))
                dict.Remove(PN_TIMESTAMP);
            if (dict.ContainsKey(PN_SESSIONKEY))
                dict.Remove(PN_SESSIONKEY);
            if (dict.ContainsKey(PN_SIGN))
                dict.Remove(PN_SIGN);
            if (dict.ContainsKey(PN_ACTION))
                dict.Remove(PN_ACTION);

            ActionRequest req = new ActionRequest(action);
            req.AddParam(dict);
            this._requests.Add(req);

            return true;
        }

        private string GenerateSignForNormal(List<string> listParamPairs)
        {
            ApiConfig.ApiInfo apiInfo = this.ApiInfo;
            listParamPairs.Add(PN_APPSECRET + "=" + apiInfo.AppSecret);
            listParamPairs.Sort();
            string str = ArrayUtil.Join<string>(listParamPairs, "&");
            str = str.ToLower();
            return StringUtil.Md5Encrypt(str).ToLower();
        }

        private bool ReadActionRequestsForCompress(string strReqCont, string appKey)
        {
            int n = strReqCont.IndexOf("#");
            int m = strReqCont.IndexOf("$");
            if (n < 0 || m < n)
            {
                this.Error(MSG_DATA_FORMAT_ERROR);
                return false;
            }

            string str = strReqCont.Substring(n + 1, m - n - 1);
            string[] arrStr = str.Split('|');
            if (arrStr.Length != 4)
            {
                this.Error(MSG_DATA_FORMAT_ERROR);
                return false;
            }

            if (String.IsNullOrEmpty(appKey))
            {
                appKey = arrStr[0];
                if (String.IsNullOrEmpty(appKey))
                {
                    this.Error(String.Format(MSG_PARAM_ERROR, "appKey"));
                    return false;
                }
            }

            string timestamp = arrStr[1];
            this._sessionKey = arrStr[2];
            string sign = arrStr[3];
            string strJson = strReqCont.Substring(m + 1);

            if (String.IsNullOrEmpty(timestamp))
            {
                this.Error(String.Format(MSG_PARAM_ERROR, "timestamp"));
                return false;
            }

            if (String.IsNullOrEmpty(sign))
            {
                this.Error(String.Format(MSG_PARAM_ERROR, "sign"));
                return false;
            }

            ApiConfig.ApiInfo apiInfo = ApiConfig.GetApiInfo(appKey);
            if (apiInfo == null)
            {
                this.Error(String.Format(MSG_APPKEY_NO_EXISTS, appKey));
                return false;
            }
            this._apiInfo = apiInfo;


            if (apiInfo.NeedVerify)
            {
                //timestamp
                string tsErrorMsg = this.VerifyTimestamp(timestamp);
                if (!String.IsNullOrEmpty(tsErrorMsg))
                {
                    this.Error(tsErrorMsg);
                    return false;
                }

                //sign
                string gSgin = this.GenerateSignForCompress(timestamp, strJson);
                if (!sign.Equals(gSgin))
                {
                    this.Error(MSG_SING_VERFIY_FAIL);
                    return false;
                }
            }

            //Requests
            try
            {
                JObject jo = JObject.Parse(strJson);
                JArray reqs = (JArray)jo["reqs"];
                foreach (JToken jt in reqs)
                {
                    JObject joReq = (JObject)jt;
                    ActionRequest req = new ActionRequest(joReq);
                    this._requests.Add(req);
                }
            }
            catch
            {
                this.Error(String.Format(MSG_PARAM_ERROR, "reqs"));
                return false;
            }

            return true;

        }

        private string GenerateSignForCompress(string timestamp, string jsonString)
        {
            StringBuilder sb = new StringBuilder();
            ApiConfig.ApiInfo apiInfo = this.ApiInfo;
            sb.Append(apiInfo.AppKey + "|");
            sb.Append(timestamp + "|");
            sb.Append(apiInfo.AppSecret + "|");
            sb.Append(jsonString);
            //sb.Append(sessionKey);

            //List<string> strList = new List<string>();
            //foreach (ActionRequest req in this._requests)
            //    strList.Add(req.ToString());
            //strList.Sort();
            //foreach (string str in strList)
            //{
            //    sb.Append("|");
            //    sb.Append(str);
            //}

            string s = sb.ToString().ToLower();
            return StringUtil.Md5Encrypt(s).ToLower();
        }

        private string VerifyTimestamp(string timestamp)
        {
            ApiConfig.ApiInfo apiInfo = this.ApiInfo;
            if (apiInfo.TimestampOffset <= 0)
                return null;

            int timestampOffset = apiInfo.TimestampOffset;

            DateTime dt;
            try
            {
                dt = DateTimeUtil.FromUnixTimeStamp(timestamp);
            }
            catch (Exception ex)
            {
                return String.Format(MSG_PARAM_ERROR, "timestamp");
            }
            DateTime dtNow = DateTime.UtcNow;
            DateTime dtStart = dtNow.AddMinutes(0 - timestampOffset);
            DateTime dtEnd = dtNow.AddMinutes(timestampOffset);
            if (dt.CompareTo(dtStart) < 0 || dt.CompareTo(dtEnd) > 0)
                return MSG_TIMESTAMP_TIMEOUT;

            return null;

        }

        #endregion

        public void Execute()
        {
            this.Execute(null, null);
        }

        public void Execute(IOSession session)
        {
            this.Execute(session.SessionKey, session);
        }

        public void Execute(string sessionKey)
        {
            this.Execute(sessionKey, null);
        }

        private void Execute(string sessionKey, IOSession session)
        {
            if (this.ApiInfo.IsHttpRequest)
            {
                this.ExecuteHttp(sessionKey);
            }
            else
            {
                if (this.Status == STATUS_OK)
                    this.ExecuteLocal(sessionKey, session);
            }
        }

        private void ExecuteLocal(string sessionKey, IOSession session)
        {
            foreach (ActionRequest req in this.Requests)
            {
                string msg;
                if (!this.BeforeExecuteLocalRequest(req, out msg))
                {
                    ActionResponse res = new ActionResponse(this);
                    res.Error(msg);
                    req.SetResponse(res);
                    continue;
                }
                this.ExecuteRequest(req, sessionKey, session);
            }
        }

        private void ExecuteRequest(ActionRequest req, string sessionKey, IOSession session)
        {
            ActionResponse res = new ActionResponse(this);
            if (!String.IsNullOrEmpty(sessionKey))
                req.SetSessionKey(sessionKey);
            req.SetResponse(res);

            string action = req.Action;
            int n = action.IndexOf(".");
            int m = action.LastIndexOf(".");
            if (n <= 0 || n == m)
            {
                res.Message = String.Format(MSG_PARAM_ERROR, "act");
                res.StatusCode = ActionResponse.STATUS_ERROR;
                return;
            }
            string assemblyName = action.Substring(0, n);
            ApiConfig.ApiAssemblyInfo assemblyInfo = this.ApiInfo.GetAssemblie(assemblyName);
            if (assemblyInfo == null)
            {
                res.Message = String.Format(MSG_ACTION_NO_EXISTS, req.Action);
                res.StatusCode = ActionResponse.STATUS_ERROR;
                return;
            }

            string typeName = assemblyInfo.Namespace + action.Substring(n, m - n) + assemblyInfo.TypeSuffix;
            /*
            string[] arrStr = typeName.Split('.');
            for (int i = 0; i < arrStr.Length; i++)
                arrStr[i] = StringUtil.FirstUpper(arrStr[i]);
            typeName = ArrayUtil.Join(arrStr, ".");
             * */
            Assembly assembly = Assembly.LoadFrom(assemblyInfo.Path);

            Type t = assembly.GetType(typeName, false, true);
            if (t == null)
            {
                res.Message = String.Format(MSG_ACTION_NO_EXISTS, req.Action);
                res.StatusCode = ActionResponse.STATUS_ERROR;
                return;
            }


            //List<ApiConfig.ApiAssemblyInfo> listAssemblyInfo = this.ApiInfo.Assemblies;

            //Type t = null;
            //foreach (ApiConfig.ApiAssemblyInfo assemblyInfo in listAssemblyInfo)
            //{
            //    string typeName = assemblyInfo.Namespace + "." + req.Action + assemblyInfo.TypeSuffix;
            //    string[] arrStr = typeName.Split('.');
            //    for (int i = 0; i < arrStr.Length; i++)
            //        arrStr[i] = StringUtility.FirstUpper(arrStr[i]);
            //    typeName = ArrayUtility.Join(arrStr, ".");
            //    Assembly assembly = Assembly.LoadFrom(assemblyInfo.Path);
            //    t = assembly.GetType(typeName, false, true);
            //    if (t != null)
            //        break;
            //}


            string methodName = StringUtil.FirstUpper(req.Action.Substring(m + 1));
            MethodInfo method = t.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
            if (method == null)
            {
                res.Message = String.Format(MSG_ACTION_NO_EXISTS, req.Action);
                res.StatusCode = ActionResponse.STATUS_ERROR;
                return;
            }

            try
            {
                Object o;
                if (session != null)
                    o = Activator.CreateInstance(t, new object[] { session });
                else
                    o = Activator.CreateInstance(t);
                method.Invoke(o, new object[] { req, res });
            }
            catch (Exception ex)
            {
                string code = Logger.Error(ex);
                res.Message = "发生系统错误：" + code;
                res.StatusCode = ActionResponse.STATUS_ERROR;
                return;
            }


            //act.Execute(req, response);

        }

        private void ExecuteHttp(string sessionKey)
        {

            StringBuilder sb = new StringBuilder();
            StringWriter sw = new StringWriter(sb);
            JsonWriter jw = new JsonTextWriter(sw);
            jw.WriteStartObject();
            //jw.WritePropertyName("ak");
            //jw.WriteValue(apiInfo.AppKey);
            //jw.WritePropertyName("ts");
            //jw.WriteValue(timestamp);
            //jw.WritePropertyName("s");
            //jw.WriteValue(sign);
            jw.WritePropertyName("reqs");
            jw.WriteStartArray();
            foreach (ActionRequest req in this.Requests)
                req.WriteJson(jw);
            jw.WriteEndArray();
            jw.WriteEndObject();
            string jsonString = sw.ToString();
            jw.Close();
            sw.Close();

            //string timestamp = DateTimeUtil.NowString;
            string timestamp = DateTimeUtil.ToUnixTimeStamp(DateTime.UtcNow);
            string sign = this.GenerateSignForCompress(timestamp, jsonString);
            ApiConfig.ApiInfo apiInfo = this.ApiInfo;
            sb = new StringBuilder();
            sb.Append("#");
            sb.Append(apiInfo.AppKey).Append("|");//AppKey
            sb.Append(timestamp).Append("|");//TimeStamp
            sb.Append(sessionKey).Append("|");//SessionKey
            sb.Append(sign).Append("$");//Sign
            sb.Append(jsonString);

            string strResult = HttpRequestor.Post(apiInfo.Url, sb.ToString());
            this._responseText = strResult;
            this.ReadActionResponses(strResult);
        }

        public void OutputResponse(Stream outputStream)
        {
            if (this._formatType == ActFormatType.Json)
            {
                if (this.ResponseType == ActResponseType.Normal)
                    this.OutputResponseForNormal(outputStream);
                else if (this.ResponseType == ActResponseType.Compress)
                    this.OutputResponseForCompress(outputStream);
            }
            else if (this._formatType == ActFormatType.Xml)
            {
                this.OutputResponseXmlForNormal(outputStream);
            }
        }

        private void OutputResponseForNormal(Stream outputStream)
        {
            JsonWriter jw = new JsonTextWriter(new StreamWriter(outputStream));
            jw.WriteStartObject();
            jw.WritePropertyName("status");
            jw.WriteValue(this.Status);
            jw.WritePropertyName("msg");
            jw.WriteValue(this.Message);
            jw.WritePropertyName("dt");
            jw.WriteValue(DateTimeUtil.NowString);
            if (this.Status == STATUS_OK && this.Requests.Count > 0)
            {
                ActionRequest req = this.Requests[0];
                if (req.Response == null)
                    throw new Exception("该请求尚未执行，无法输出结果");
                ActionResponse res = req.Response;
                jw.WritePropertyName("actStatus");
                jw.WriteValue(res.StatusCode);
                jw.WritePropertyName("actMsg");
                jw.WriteValue(res.Message);
                jw.WritePropertyName("rps");
                List<ActionResult> results = res.Results;
                if (results.Count > 0)
                {
                    ActionResult firstResult = results[0];
                    this.OutputResultForNormal(jw, res, firstResult, firstResult.AllValues, true);
                }
                if (res.HasResult("listAttr"))
                {
                    ActionResult rsListAttr = res.GetResult("listAttr");
                    if (rsListAttr.Count > 0)
                    {
                        Dictionary<string, object> dict = rsListAttr.GetDict(0);
                        jw.WritePropertyName("listAttr");
                        jw.WriteStartObject();
                        foreach (KeyValuePair<string, object> pair in dict)
                        {
                            jw.WritePropertyName(pair.Key);
                            jw.WriteValue(pair.Value);
                        }
                        jw.WriteEndObject();
                    }
                }
            }
            jw.WriteEndObject();
            jw.Close();
        }
        private void OutputResultForNormal(JsonWriter jw, ActionResponse res, ActionResult result, List<object[]> valuesList, bool isList)
        {
            int i = 0;

            if (isList)
                jw.WriteStartArray();
            else
                jw.WriteStartObject();

            foreach (object[] values in valuesList)
            {
                i = 0;
                if (isList)
                    jw.WriteStartObject();
                foreach (string fname in result.Fields)
                {
                    jw.WritePropertyName(fname);

                    ResultRelation rel = result.GetRelationByField(fname);
                    if (rel != null)
                    {
                        string relationField = rel.RelationField;
                        int fieldIndex = result.GetFieldIndex(relationField);
                        if (fieldIndex >= 0 && fieldIndex < values.Length)
                        {
                            object val = values[fieldIndex];
                            string aimingFieldName = rel.AimingField;
                            ActionResult rsAiming = res.GetResult(rel.AimingResult);
                            if (rsAiming != null)
                            {
                                if (val == null)
                                    jw.WriteNull();
                                else
                                {
                                    List<object[]> aimingValues = rsAiming.GetValues(aimingFieldName, val);
                                    bool aimingIsList = rel != null && rel.Type == ResultRelationType.Multiple;
                                    this.OutputResultForNormal(jw, res, rsAiming, aimingValues, aimingIsList);
                                }
                            }
                        }
                    }
                    else
                    {
                        jw.WriteValue(values[i]);
                    }
                    i++;
                }

                if (isList)
                    jw.WriteEndObject();
                else
                    break;
            }

            if (isList)
                jw.WriteEndArray();
            else
                jw.WriteEndObject();

        }

        private void OutputResponseXmlForNormal(Stream outputStream)
        {
            XmlDocument xmlDoc = new XmlDocument();
            XmlElement elRoot = xmlDoc.CreateElement("response");
            xmlDoc.AppendChild(elRoot);

            XmlElement elStatus = xmlDoc.CreateElement("status");
            elStatus.InnerText = this.Status;
            elRoot.AppendChild(elStatus);

            XmlElement elMsg = xmlDoc.CreateElement("msg");
            elMsg.InnerText = this.Message;
            elRoot.AppendChild(elMsg);

            XmlElement elDt = xmlDoc.CreateElement("dt");
            elDt.InnerText = DateTimeUtil.NowString;

            if (this.Status == STATUS_OK && this.Requests.Count > 0)
            {
                ActionRequest req = this.Requests[0];
                if (req.Response == null)
                    throw new Exception("该请求尚未执行，无法输出结果");
                ActionResponse res = req.Response;

                XmlElement elActSt = xmlDoc.CreateElement("actStatus");
                elActSt.InnerText = res.StatusCode.ToString();
                elRoot.AppendChild(elActSt);

                XmlElement elActMsg = xmlDoc.CreateElement("actMsg");
                elActMsg.InnerText = res.Message;
                elRoot.AppendChild(elActMsg);

                XmlElement elRps = xmlDoc.CreateElement("rps");
                elRoot.AppendChild(elRps);

                List<ActionResult> results = res.Results;
                if (results.Count > 0)
                {
                    ActionResult firstResult = results[0];
                    this.OutputResultXmlForNormal(xmlDoc, elRps, res, firstResult, firstResult.AllValues, true);
                }
                if (res.HasResult("listAttr"))
                {
                    ActionResult rsListAttr = res.GetResult("listAttr");
                    if (rsListAttr.Count > 0)
                    {
                        Dictionary<string, object> dict = rsListAttr.GetDict(0);
                        XmlElement elListAttr = xmlDoc.CreateElement("listAttr");
                        elRoot.AppendChild(elListAttr);
                        foreach (KeyValuePair<string, object> pair in dict)
                        {
                            XmlElement el = xmlDoc.CreateElement(pair.Key);
                            el.InnerText = pair.Value.ToString();
                            elListAttr.AppendChild(el);
                        }
                    }
                }
            }

            XmlTextWriter writer = new XmlTextWriter(outputStream, Encoding.UTF8);
            xmlDoc.WriteTo(writer);
            writer.Flush();
        }
        private void OutputResultXmlForNormal(XmlDocument xmlDoc, XmlElement el, ActionResponse res, ActionResult result, List<object[]> valuesList, bool isList)
        {
            XmlElement elEntItem = el;

            int i = 0;

            foreach (object[] values in valuesList)
            {
                i = 0;
                if (isList)
                {
                    XmlElement elItem = xmlDoc.CreateElement("item");
                    el.AppendChild(elItem);
                    elEntItem = elItem;
                }
                //jw.WriteStartObject();
                foreach (string fname in result.Fields)
                {
                    XmlElement elAttr = xmlDoc.CreateElement(fname);
                    elEntItem.AppendChild(elAttr);

                    ResultRelation rel = result.GetRelationByField(fname);
                    if (rel != null)
                    {
                        string relationField = rel.RelationField;
                        int fieldIndex = result.GetFieldIndex(relationField);
                        if (fieldIndex >= 0 && fieldIndex < values.Length)
                        {
                            object val = values[fieldIndex];
                            string aimingFieldName = rel.AimingField;
                            ActionResult rsAiming = res.GetResult(rel.AimingResult);
                            if (rsAiming != null)
                            {
                                if (val != null)
                                {
                                    List<object[]> aimingValues = rsAiming.GetValues(aimingFieldName, val);
                                    bool aimingIsList = rel != null && rel.Type == ResultRelationType.Multiple;
                                    this.OutputResultXmlForNormal(xmlDoc, elAttr, res, rsAiming, aimingValues, aimingIsList);
                                }
                            }
                        }
                    }
                    else
                    {
                        elAttr.InnerText = Convert.ToString(values[i]);
                    }
                    i++;
                }

                if (!isList)
                    break;
            }

        }

        private void OutputResponseForCompress(Stream outputStream)
        {
            JsonWriter jw = new JsonTextWriter(new StreamWriter(outputStream));
            jw.WriteStartObject();
            jw.WritePropertyName("st");
            jw.WriteValue(this.Status);
            jw.WritePropertyName("msg");
            jw.WriteValue(this.Message);
            jw.WritePropertyName("dt");
            jw.WriteValue(DateTimeUtil.NowString);
            jw.WritePropertyName("rps");
            jw.WriteStartArray();
            if (this.Status == STATUS_OK)
            {
                foreach (ActionRequest req in this.Requests)
                {
                    if (req.Response == null)
                        throw new Exception("该请求尚未执行，无法输出结果");
                    req.Response.WriterOutputForCompress(jw);
                }
            }
            jw.WriteEndArray();
            jw.WriteEndObject();
            jw.Close();
        }

        private bool ReadActionResponses(string strResult)
        {
            JsonReader jr = new JsonTextReader(new StringReader(strResult));

            while (jr.Read())
            {
                if (jr.TokenType == JsonToken.PropertyName)
                {
                    string propName = Convert.ToString(jr.Value);
                    jr.Read();
                    if (propName == "st")
                    {
                        this._status = Convert.ToString(jr.Value);
                    }
                    else if (propName == "msg")
                    {
                        this._message = Convert.ToString(jr.Value);
                    }
                    else if (propName == "dt")
                    {
                        this._remoteDateTime = Convert.ToDateTime(jr.Value);
                    }
                    else if (propName == "rps")
                    {
                        int i = 0;
                        while (jr.Read())
                        {
                            if (jr.TokenType == JsonToken.StartObject)
                            {
                                ActionResponse response = new ActionResponse(this);
                                response.ReadInput(jr);
                                this.Requests[i].SetResponse(response);
                                i++;
                            }
                            else if (jr.TokenType == JsonToken.EndArray)
                                break;
                        }
                    }
                }
            }
            jr.Close();
            return true;
        }


        private void Error(String message)
        {
            this._status = STATUS_ERROR;
            this._message = message;
        }

        public void SessionInvalid()
        {
            this._status = STATUS_SESSION_KEY_INVALID;
        }

        protected virtual bool BeforeExecuteLocalRequest(ActionRequest req, out string msg)
        {
            msg = "";
            return true;
        }

    }

    public enum ActRequestType
    {
        Normal,
        Compress
    }

    public enum ActResponseType
    {
        Normal,
        Compress
    }

    public enum ActFormatType
    {
        Json,
        Xml
    }
}
